<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Generator 函数语法</title>

</head>

<body>
    <script>
        // 执行 Generator 函数会返回一个遍历器对象，也就是说，Generator 函数除了状态机，还是一个遍历器对象生成函数。返回的遍历器对象，可以依次遍历 Generator 函数内部的每一个状态。

        // 形式上，Generator 函数是一个普通函数，但是有两个特征。一是，function关键字与函数名之间有一个星号；二是，函数体内部使用yield表达式，定义不同的内部状态（yield在英语里的意思就是“产出”）。

        function* helloWorldGenerator() {
            yield 'hello';
            yield 'world';
            return 'ending'
        }
        var hw = helloWorldGenerator()

        // 然后，Generator 函数的调用方法与普通函数一样，也是在函数名后面加上一对圆括号。不同的是，调用 Generator 函数后，该函数并不执行，返回的也不是函数运行结果，而是一个指向内部状态的指针对象，也就是上一章介绍的遍历器对象（Iterator Object）。

        // 下一步，必须调用遍历器对象的next方法，使得指针移向下一个状态。也就是说，每次调用next方法，内部指针就从函数头部或上一次停下来的地方开始执行，直到遇到下一个yield表达式（或return语句）为止。换言之，Generator 函数是分段执行的，yield表达式是暂停执行的标记，而next方法可以恢复执行。

        console.log(hw.next())
        console.log(hw.next())
        console.log(hw.next())
        console.log(hw.next())

        // yield 表达式
        // 由于 Generator 函数返回的遍历器对象，只有调用next方法才会遍历下一个内部状态，所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

        //yield表达式与return语句既有相似之处，也有区别。相似之处在于，都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield，函数暂停执行，下一次再从该位置继续向后执行，而return语句不具备位置记忆的功能。一个函数里面，只能执行一次（或者说一个）return语句，但是可以执行多次（或者说多个）yield表达式。正常函数只能返回一个值，因为只能执行一次return；Generator 函数可以返回一系列的值，因为可以有任意多个yield。从另一个角度看，也可以说 Generator 生成了一系列的值，这也就是它的名称的来历（英语中，generator 这个词是“生成器”的意思）。

        // Generator 函数可以不用yield表达式，这时就变成了一个单纯的暂缓执行函数。

        function* f() {
            console.log('执行了')
        }

        var generator = f();

        setTimeout(() => {
            generator.next()
        }, 2000)
        console.log('next 之前是不执行的的奥')

        // 另外需要注意，yield表达式只能用在 Generator 函数里面，用在其他地方都会报错。

        // next 方法的参数
        // yield表达式本身没有返回值，或者说总是返回undefined。next方法可以带一个参数，该参数就会被当作上一个yield表达式的返回值。
        function* f() {
            for (var i = 0; true; i++) {
                var reset = yield i;
                console.log("i", i)
                if (reset) {
                    i = -1
                    console.log('reset::', i)

                }
            }
            //上面的其实是无限循环 定义了很多的 yield 表达式 
            // 可以是：： 
            // yield 0
            // yield 1
            // yield 2
            // yield 3
            // yield 4
            // yield 5
            // 直到reset w为 true i =-1 
            // 就定义为 yield 0 再开始
        }
        var g = f()
        console.log(g.next())
        console.log(g.next())
        console.log(g.next(true))
        console.log(g.next())
        console.log(g.next())
        console.log(g.next())
        console.log(g.next(true))
        // next() 参数 会成为 yield 表达式的返回值

        // 上面代码先定义了一个可以无限运行的 Generator 函数f，如果next方法没有参数，每次运行到yield表达式，变量reset的值总是undefined。当next方法带一个参数true时，变量reset就被重置为这个参数（即true），因此i会等于-1，下一轮循环就会从-1开始递增。

        //  其实没太明白 下一轮从0 开始 为什么传true 的这一轮就是从0 开始了呢

        // 这个功能有很重要的语法意义，Generator函数从暂停状态恢复到运行，它的上下文状态(context)是不变的。通过next 方法的参数，就有办法在Generator函数开始运行之后，继续向函数体内部注入值。也就是说，可以在Generator函数运行的不同阶段，从外部向内部注入不同的值，从而调整函数行为。


        console.log('====================')

        function* foo(x) {
            var y = 2 * (yield(x + 1));
            var z = yield(y / 3)
            return (x + y + z)
        }
        var a = foo(5)
        console.log(a.next())
        var a = foo(5);
        console.log(a.next())
        console.log(a.next())
        console.log(a.next())

        // a.next() // Object{value:6, done:false}
        // a.next() // Object{value:NaN, done:false}
        // a.next() // Object{value:NaN, done:true}

        var b = foo(5);
        console.log(a.next())
        console.log(b.next(12))
        console.log(b.next(13))

        // b.next() // { value:6, done:false }
        // b.next(12) // { value:8, done:false }
        // b.next(13) // { value:42, done:true }
        //3 for...of 循环
        function* foo2() {
            yield 1;
            yield 2;
            yield 3;
            yield 4;
            yield 5;
            return 6;
        }

        for (let v of foo2()) {
            console.log(v);
        }
        // 1 2 3 4 5

        // 下面是一个利用 Generator 函数和for...of循环，实现斐波那契数列的例子。
        function* fibonacci() {
            let [prev, curr] = [0, 1]
            for (;;) {
                yield curr;
                [prev, curr] = [curr, prev + curr];
            }
        }
        for (let n of fibonacci()) {
            if (n > 1000) break;
            console.log(n)
        }

        //利用for...of 循环，可以写出遍历任意对象（object）的方法，原生的JavaScript 对象没有遍历接口，无法使用 for...of循环，通过Generator函数加上这个接口

        function* objectEntries(obj) {
            let propKeys = Reflect.ownKeys(obj);
            for (let propKey of propKeys) {
                yield [propKey, obj[propKey]]
            }
        }
        let jane = {
            first: 'Jane',
            last: 'Doe'
        }
        for (let [key, value] of objectEntries(jane)) {
            console.log(`${key}:${value}`)
        }

        // Generator 可以暂停函数执行，返回任意表达式的值。这种特点使得 Generator 有多种应用场景。
    </script>
</body>

</html>